﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace NMF.Expressions
{
    internal class ObservableNewExpression<T1, T> : ObservableStaticMethodCall<T1, T>
    {
        public ObservableNewExpression(NewExpression node, ObservableExpressionBinder binder)
            : base(CreateDelegate(node), binder.VisitObservable<T1>(node.Arguments[0])) { }

        private static Func<T1, T> CreateDelegate(NewExpression node)
        {
            var p1 = Expression.Parameter(node.Arguments[0].Type, "p1");
            var expression = Expression.Lambda<Func<T1, T>>(
                Expression.New(node.Constructor, p1), p1);
            return expression.Compile();
        }
    }

    internal class ObservableNewExpression<T1, T2, T> : ObservableStaticMethodCall<T1, T2, T>
    {
        public ObservableNewExpression(NewExpression node, ObservableExpressionBinder binder)
            : base(CreateDelegate(node), binder.VisitObservable<T1>(node.Arguments[0]), binder.VisitObservable<T2>(node.Arguments[1])) { }

        private static Func<T1, T2, T> CreateDelegate(NewExpression node)
        {
            var p1 = Expression.Parameter(node.Arguments[0].Type, "p1");
            var p2 = Expression.Parameter(node.Arguments[1].Type, "p2");
            var expression = Expression.Lambda<Func<T1, T2, T>>(
                Expression.New(node.Constructor, p1, p2), p1, p2);
            return expression.Compile();
        }

    }

    internal class ObservableNewExpression<T1, T2, T3, T> : ObservableStaticMethodCall<T1, T2, T3, T>
    {
        public ObservableNewExpression(NewExpression node, ObservableExpressionBinder binder)
            : base(CreateDelegate(node),
                binder.VisitObservable<T1>(node.Arguments[0]),
                binder.VisitObservable<T2>(node.Arguments[1]),
                binder.VisitObservable<T3>(node.Arguments[2])) { }

        private static Func<T1, T2, T3, T> CreateDelegate(NewExpression node)
        {
            var p1 = Expression.Parameter(node.Arguments[0].Type, "p1");
            var p2 = Expression.Parameter(node.Arguments[1].Type, "p2");
            var p3 = Expression.Parameter(node.Arguments[2].Type, "p3");
            var expression = Expression.Lambda<Func<T1, T2, T3, T>>(
                Expression.New(node.Constructor, p1, p2, p3), p1, p2, p3);
            return expression.Compile();
        }

    }

    internal class ObservableNewExpression<T1, T2, T3, T4, T> : ObservableStaticMethodCall<T1, T2, T3, T4, T>
    {
        public ObservableNewExpression(NewExpression node, ObservableExpressionBinder binder)
            : base(CreateDelegate(node),
                binder.VisitObservable<T1>(node.Arguments[0]),
                binder.VisitObservable<T2>(node.Arguments[1]),
                binder.VisitObservable<T3>(node.Arguments[2]),
                binder.VisitObservable<T4>(node.Arguments[3])) { }

        private static Func<T1, T2, T3, T4, T> CreateDelegate(NewExpression node)
        {
            var p1 = Expression.Parameter(node.Arguments[0].Type, "p1");
            var p2 = Expression.Parameter(node.Arguments[1].Type, "p2");
            var p3 = Expression.Parameter(node.Arguments[2].Type, "p3");
            var p4 = Expression.Parameter(node.Arguments[3].Type, "p4");
            var expression = Expression.Lambda<Func<T1, T2, T3, T4, T>>(
                Expression.New(node.Constructor, p1, p2, p3, p4), p1, p2, p3, p4);
            return expression.Compile();
        }

    }
}
